﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.ComponentModel;

namespace Prologis.ShifterNT.Extensions
{
    public static class XmlExtensions
    {


        public static XElement ToXElement(this XmlElement xml)
        {
            XmlDocument doc = new XmlDocument();

            doc.AppendChild(doc.ImportNode(xml, true));

            return XElement.Parse(doc.InnerXml);

        }


        /// <summary>
        /// Determines whether the specified element has element.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="name">The name.</param>
        /// <returns>
        ///   <c>true</c> if the specified element has element; otherwise, <c>false</c>.
        /// </returns>
        public static bool HasElement(this XElement element, string name)
        {
            return element.Element(name) != null;
        }


        /// <summary>
        /// Determines whether the specified element has attribute.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="name">The name.</param>
        /// <returns>
        ///   <c>true</c> if the specified element has attribute; otherwise, <c>false</c>.
        /// </returns>
        public static bool HasAttribute(this XElement element, string name)
        {
            return element.Attribute(name) != null;
        }

        /// <summary>
        /// Determines whether the specified element has attributes.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns>
        ///   <c>true</c> if the specified element has attributes; otherwise, <c>false</c>.
        /// </returns>
        public static bool HasAttributes(this XElement element)
        {
            return element.Attributes().Count() > 0;
        }


        /// <summary>
        /// Determines whether the specified element has attribute.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="elementName">Name of the element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <returns>
        ///   <c>true</c> if the specified element has attribute; otherwise, <c>false</c>.
        /// </returns>
        public static bool HasAttribute(this XElement element, string elementName, string attributeName)
        {
            if (element.Element(elementName) != null)
            {
                if (element.Element(elementName).Attribute(attributeName) != null)
                {
                    return element.Element(elementName).Attribute(attributeName) != null;
                }
            }
            return false;
        }


        /// <summary>
        /// Gets the attribute.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static XAttribute GetAttribute(this XElement element, string attributeName, object defaultValue)
        {
            if (element.Attribute(attributeName) != null)
            {
                return element.Attribute(attributeName);
            }
            return new XAttribute(attributeName, defaultValue);
        }


        /// <summary>
        /// Gets the attribute.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <returns></returns>
        public static XAttribute GetAttribute(this XElement element, string attributeName)
        {

            object obj = new object();
            obj = "";

            return GetAttribute(element, attributeName, obj);
        }


        /// <summary>
        /// Gets the attribute value.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <returns></returns>
        public static string GetAttributeValue(this XElement element, string attributeName)
        {
            if (element.Attribute(attributeName) != null)
            {
                return element.Attribute(attributeName).Value;
            }
            else
            {
                return "";
            }
        }



        /// <summary>
        /// Gets the attribute.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="elementName">Name of the element.</param>
        /// <param name="attributeName">Name of the attribute.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static XAttribute GetAttribute(this XElement element, string elementName, string attributeName, object defaultValue)
        {
            if (element.Element(elementName) != null)
            {
                if (element.Element(elementName).Attribute(attributeName) != null)
                {
                    return element.Element(elementName).Attribute(attributeName);
                }
            }
            return new XAttribute(attributeName, defaultValue);
        }


        public static XAttribute GetAttribute(this XElement element, string elementName, string attributeName)
        {
            //if (element.Element(elementName) != null) {
            //    if (element.Element(elementName).Attribute(attributeName) != null) {
            //        return element.Element(elementName).Attribute(attributeName);
            //    }
            //}
            //return null;

            return GetAttribute(element, elementName, attributeName, "");
        }




        /// <summary>
        /// Ensures the element.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="elementName">Name of the element.</param>
        /// <returns></returns>
        public static XElement EnsureElement(this XElement element, string elementName)
        {
            XElement ensured = element.Element(elementName);
            if (ensured == null)
            {
                ensured = new XElement(elementName);
                element.Add(ensured);
            }
            return ensured;
        }




        /// <summary>
        /// Gets the attribute.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static T GetAttribute<T>(XmlElement element, string name, T _default)
        {
            foreach (XmlAttribute attribute in element.Attributes)
            {
                if (attribute.Name == name)
                {
                    T result = ChangeType<T>(attribute.Value);
                    return result;
                }
            }
            return _default;
        }


        /// <summary>
        /// Gets the attribute ensured.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="element">The element.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static T GetAttributeEnsured<T>(XmlElement element, string name)
        {
            foreach (XmlAttribute attribute in element.Attributes)
            {
                if (attribute.Name == name)
                {
                    T result = ChangeType<T>(attribute.Value);
                    return result;
                }
            }
            throw new Exception("Attribute [" + name + "] is not available!");
        }

        /// <summary>
        /// Changes the type.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="_default">The _default.</param>
        /// <returns></returns>
        public static T ChangeType<T>(object value)
        {
            if (value.GetType() == typeof(T)) { return (T)value; }
            if (typeof(T).IsEnum) { return (T)Enum.Parse(typeof(T), (string)value, true); }
            if (value is IConvertible)
            {
                Type conversionType = typeof(T);
                if (IsNullableType(typeof(T)))
                {
                    NullableConverter nullableConverter = new NullableConverter(typeof(T));
                    conversionType = nullableConverter.UnderlyingType;
                    return (T)Convert.ChangeType(value, conversionType);
                }
                else
                {
                    return (T)Convert.ChangeType(value, conversionType);
                }
            }
            throw new Exception("Conversion of '" + value.ToString() + "' to type [" + typeof(T).Name + "] is not allowed!");
        }


        /// <summary>
        /// Determines whether [is nullable type] [the specified the type].
        /// </summary>
        /// <param name="theType">The type.</param>
        /// <returns>
        /// 	<c>true</c> if [is nullable type] [the specified the type]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsNullableType(Type type)
        {
            return (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
        }




    }
}
